// Copyright (c) XiGuan 2023
// Author： xiguan
// Email: xiguan.teng@qq.com
// Create on 2023/11/7
// TODO:
//

#pragma once
#ifndef WEBSERVER_POOLS_THREADPOOL_H
#define WEBSERVER_POOLS_THREADPOOL_H

#include <assert.h>

#include <condition_variable>
#include <functional>
#include <mutex>
#include <queue>
#include <thread>

class ThreadPool {
 private:
  struct Pool {
    std::mutex mtx_;
    std::condition_variable cond_;
    std::queue<std::function<void()>> tasks_;
    bool is_closed_;
  };

  std::shared_ptr<Pool> pool_;

 public:
  explicit ThreadPool(size_t thread_count = 0)
      : pool_(std::make_shared<Pool>()) {
    assert(thread_count > 0);
    for (auto idx = 0; idx < thread_count; ++idx) {
      std::thread([pool = pool_]() {
        std::unique_lock<std::mutex> locker(pool->mtx_);
        while (true) {
          if (!pool->tasks_.empty()) {
            auto task = std::move(pool->tasks_.front());
            pool->tasks_.pop();
            locker.unlock();
            task();
            locker.lock();
          } else if (pool->is_closed_) {
            break;
          } else {
            pool->cond_.wait(locker);
          }
        }
      }).detach();
    }
  }

  ThreadPool() = default;
  ThreadPool(ThreadPool &&) = default;
  ~ThreadPool() {
    if (static_cast<bool>(pool_)) {
      {
        std::lock_guard<std::mutex> locker(pool_->mtx_);
        pool_->is_closed_ = true;
      }
      pool_->cond_.notify_all();
    }
  }

  template <class F>
  void add_task(F &&_task) {
    {
      std::lock_guard<std::mutex> locker(pool_->mtx_);
      pool_->tasks_.emplace(std::forward<F>(_task));
    }
    pool_->cond_.notify_one();
  }
};

#endif  // WEBSERVER_POOLS_THREADPOOL_H
